/**
 * This file is part of AppStoreBot.
 *
 *  AppStoreBot is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  AppStoreBot is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with AppStoreBot.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package lu.pragmaconsult.appstorebot.itunes;

import static lu.pragmaconsult.appstorebot.itunes.Constants.AJAX_NAME;
import static lu.pragmaconsult.appstorebot.itunes.Constants.ATT_VALUE;
import static lu.pragmaconsult.appstorebot.itunes.Constants.DATE_FORMAT;
import static lu.pragmaconsult.appstorebot.itunes.Constants.DATE_LIST_AVAILABLE_DAYS;
import static lu.pragmaconsult.appstorebot.itunes.Constants.DATE_LIST_AVAILABLE_WEEKS;
import static lu.pragmaconsult.appstorebot.itunes.Constants.HEADER_CONTENT_DISPOSITION;
import static lu.pragmaconsult.appstorebot.itunes.Constants.SALES_URL;
import static lu.pragmaconsult.appstorebot.itunes.Constants.SELECT_NAME;
import static lu.pragmaconsult.appstorebot.itunes.Constants.VIEW_STATE;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.zip.GZIPInputStream;

import lu.pragmaconsult.appstorebot.BotContext;
import lu.pragmaconsult.appstorebot.ExitStatus;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;

/**
 * TODO: refactoring and split classes into several steps
 * @author Wilfried Vandenberghe (wilfried.vandenberghe at pragmaconsult.lu)
 */
class DownloadReportBotState implements BotState {

	private static final Logger logger = LoggerFactory.getLogger(DownloadReportBotState.class);

	/* (non-Javadoc)
	 * @see lu.pragmaconsult.appstorebot.itunes.BotState#run()
	 */
	@Override
	public ExitStatus run() {
		BotContext context = AbstractBotState.getBot().getBotContext();
		
		if(context.isVerbose()) {
			logger.info("Downloading sales reports.");
		}
		
		try {
			SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);
			List<String> availableDateReport = Arrays.asList(context.getContextAttribute(DATE_LIST_AVAILABLE_DAYS).split(","));
			
			// populate report days 
			ArrayList<Date> reportDates = new ArrayList<Date>();
			if(context.getDate() != null && context.getDate().length() != 0) {
				reportDates.add(format.parse(context.getDate()));
			} else {
				Calendar calendar = Calendar.getInstance();
				calendar.setTime(new Date());
				for(int i = 1; i <= context.getDays(); i++) {
					calendar.add(Calendar.DAY_OF_YEAR, -1);
					
					reportDates.add(calendar.getTime());
				}
			}
			
			// for each report date 
			for (Date date : reportDates) {
				// check if the report is available
				String dateStr = format.format(date);
				if(availableDateReport.contains(dateStr)) {
					if(context.isVerbose()) {
						logger.info("Downloading report for: " + dateStr);
					}
					
					HttpClient httpClient = AbstractBotState.getHttpClient();
					
					// Before downloading
					PostMethod beforeDownload = new PostMethod(SALES_URL);
					
					NameValuePair ajaxRequest = new NameValuePair("AJAXREQUEST", context.getContextAttribute(AJAX_NAME));
					NameValuePair theForm = new NameValuePair("theForm", "theForm");
					NameValuePair theFormNotNormal = new NameValuePair("theForm:xyz", "notnormal");
					NameValuePair theFormVendorType = new NameValuePair("theForm:vendorType", "Y");
					NameValuePair dateDay1 = new NameValuePair("theForm:datePickerSourceSelectElementSales", dateStr);
					NameValuePair dateDay2 = new NameValuePair("theForm:datePickerSourceSelectElementSales", dateStr);
					NameValuePair dateWeek = new NameValuePair("theForm:weekPickerSourceSelectElement", context.getContextAttribute(DATE_LIST_AVAILABLE_WEEKS).split(",")[0]);
					NameValuePair viewState = new NameValuePair("javax.faces.ViewState", context.getContextAttribute(VIEW_STATE));
					NameValuePair select = new NameValuePair(context.getContextAttribute(SELECT_NAME), context.getContextAttribute(SELECT_NAME));
					beforeDownload.setRequestBody(new NameValuePair[] {ajaxRequest, theForm, theFormNotNormal, theFormVendorType, dateDay1, dateDay2, dateWeek, viewState, select});
					
					httpClient.executeMethod(beforeDownload);
					
					HtmlParser parser = new HtmlParser(beforeDownload.getResponseBodyAsStream());
					
					// TODO: replace following by XPath expression
					Node node = parser.getDocument().getChildNodes().item(0).getChildNodes().item(1).getChildNodes().item(6).getChildNodes().item(1).getAttributes().getNamedItem(ATT_VALUE);
					context.setContextAttribute(VIEW_STATE, node.getNodeValue());
					
					PostMethod postDownload = new PostMethod(SALES_URL);

					// Prepare form download parameters
					NameValuePair dateDay = new NameValuePair("theForm:datePickerSourceSelectElementSales", dateStr);
					viewState = new NameValuePair("javax.faces.ViewState", context.getContextAttribute(VIEW_STATE));
					NameValuePair downloadLabel = new NameValuePair("theForm:downloadLabel2", "theForm:downloadLabel2");
					postDownload.setRequestBody(new NameValuePair[] { theForm, theFormNotNormal, theFormVendorType, dateDay, dateWeek, viewState, downloadLabel });
					
					httpClient.executeMethod(postDownload);
					
					Header filename = postDownload.getResponseHeader(HEADER_CONTENT_DISPOSITION);
					
					// create output file
					
					// compute filename
					String filenameStr = filename.getValue().split("=")[1];
					
					if(context.isVerbose()) {
						logger.info("File downloaded: " + filenameStr);
					}
					
					if(context.isUnzip()) {
						filenameStr = filenameStr.substring(0, filenameStr.length() - 3);
					}
					
					File f = null;
					if(context.getOutputDiretory() != null) {
						f = new File(context.getOutputDiretory(), filenameStr);
					} else {
						f = new File(filenameStr);
					}
					f.createNewFile();
					
					// create input stream
					OutputStream out = new BufferedOutputStream(new FileOutputStream(f));
					InputStream in = null;
					if(context.isUnzip()) {
						in = new GZIPInputStream(postDownload.getResponseBodyAsStream());
					} else {
						in = postDownload.getResponseBodyAsStream();
					}
					
					byte[] buffer = new byte[1024];
					int readSize;
					while((readSize = in.read(buffer)) != -1) {
						out.write(buffer, 0, readSize);
					}
					
					out.close();
				}
				
			}
		} catch (ParseException e) {
			return ExitStatus.FAILURE;
		} catch (HttpException e) {
			return ExitStatus.FAILURE;
		} catch (IOException e) {
			return ExitStatus.FAILURE;
		} catch (Exception e) {
			return ExitStatus.FAILURE;
		}
		
		return ExitStatus.COMPLETE;
	}

}
